// Copyright 2016-2024, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package state

import (
	"testing"

	"github.com/pulumi/pulumi/pkg/v3/backend/display"
	"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
	"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// TestRenameProvider tests that we can rename a provider and produce a valid snapshot.
func TestRenameProvider(t *testing.T) {
	t.Parallel()

	// Define a state with a single provider and a single resource dependent on that provider.
	provURN := resource.URN("urn:pulumi:dev::prog-aws-typescript::pulumi:providers:random::my-provider")
	prov := resource.State{
		URN:    provURN,
		ID:     "81cd12dd-2a90-4f21-a521-f4c71c1f11eb",
		Type:   "pulumi:providers:random",
		Custom: true,
	}

	providerRefString := string(prov.URN) + "::" + string(prov.ID)

	res1 := resource.State{
		URN:      resource.URN("urn:pulumi:dev::prog-aws-typescript::random:index/randomPet:RandomPet::pet-0"),
		Type:     "random:index/randomPet:RandomPet",
		Provider: providerRefString,
	}
	snap := &deploy.Snapshot{
		Resources: []*resource.State{
			&prov,
			&res1,
		},
	}

	// Ensure that the snapshot is valid before the rename.
	require.NoError(t, snap.VerifyIntegrity())

	// Mutates the snapshot.
	err := stateRenameOperation(provURN, "our-provider", display.Options{}, snap)
	require.NoError(t, err)

	// Ensure that the snapshot is valid after the rename.
	require.NoError(t, snap.VerifyIntegrity())

	// Check that the snapshot contains the renamed provider as `our-provider`.
	for _, res := range snap.Resources {
		if res.ID == prov.ID {
			assert.Equal(t, "our-provider", res.URN.Name())
		}
	}
}

// Regression test for https://github.com/pulumi/pulumi/issues/13179.
//
// Defines a state with a two resources, one parented to the other,
// and renames the parent.
// The child must have an updated parent reference.
func TestStateRename_updatesChildren(t *testing.T) {
	t.Parallel()

	provider := resource.URN("urn:pulumi:dev::pets::random::provider")
	parent := resource.URN("urn:pulumi:dev::pets::random:index/randomPet:RandomPet::parent")
	child := resource.URN("urn:pulumi:dev::pets::random:index/randomPet:RandomPet$random:index/randomPet:RandomPet::child")

	snap := deploy.Snapshot{
		Resources: []*resource.State{
			{
				URN:    provider,
				ID:     "provider-id",
				Type:   "pulumi:provider:random",
				Custom: true,
			},
			{
				URN:    parent,
				ID:     "parent-id",
				Type:   "random:index/randomPet:RandomPet",
				Custom: true,
			},
			{
				URN:    child,
				ID:     "child-id",
				Type:   "random:index/randomPet:RandomPet",
				Parent: parent,
				Custom: true,
			},
		},
	}
	require.NoError(t, snap.VerifyIntegrity(),
		"invalid test: snapshot is already broken")

	err := stateRenameOperation(parent, "new-parent", display.Options{}, &snap)
	require.NoError(t, err)

	require.NoError(t, snap.VerifyIntegrity(), "snapshot is broken after rename")

	// VerifyIntegrity checks that the parent reference is updated,
	// but we'll check it explicitly here to be sure.
	var sawChild bool
	for _, res := range snap.Resources {
		if res.URN == child {
			sawChild = true
			assert.Equal(t, "new-parent", res.Parent.Name())
		}
	}
	assert.True(t, sawChild, "child resource not found in snapshot")
}
